﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Reflection;

namespace nwrole.console
{
    public class CommandBase
    {
        public static Dictionary<string, object> Variables = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

        public CommandBase Print (string text, params object[] fmts)
        {
            Console.Write(String.Format(text, fmts));
            return this;
        }

        public CommandBase PrintLine (string text, params object[] fmts)
        {
            this.Print(text + "\r\n", fmts);
            return this;
        }

        public CommandBase PrintException (Exception exception)
        {
            this.PrintLine("")
                .PrintLine(ConsoleColor.Red, "[Exception:" + exception.GetType().ToString() + "]")
                .PrintLine("\t" + "Message:".PadRight(15) + exception.Message)
                .PrintLine("\t" + "Trace:".PadRight(15) + exception.StackTrace)
                .PrintLine("\t" + "Source:".PadRight(15) + exception.Source)
                .PrintLine(ConsoleColor.Red, "[/Exception]");
            return this;
        }

        public CommandBase Print (ConsoleColor color, string text, params object[] fmts)
        {
            ConsoleColor temp = Console.ForegroundColor;
            Console.ForegroundColor = color;
            this.Print(text, fmts);
            Console.ForegroundColor = temp;
            return this;
        }

        public CommandBase PrintIndented(int indention, string text, params object[] fmts)
        {
            return this.Print("".PadRight(indention) + text, fmts);
        }

        public CommandBase PrintIndented(ConsoleColor color, int indention, string text, params object[] fmts)
        {
            return this.Print(color, "".PadRight(indention) + text, fmts);
        }

        public CommandBase PrintLine (ConsoleColor color, string text, params object[] fmts)
        {
            return this.Print(color, text + "\r\n", fmts);
        }

        public CommandBase PrintIndentedLine(int indention, string text, params object[] fmts)
        {
            return this.PrintIndented(indention, text + "\r\n", fmts);
        }

        public CommandBase PrintIndentedLine(ConsoleColor color, int indention, string text, params object[] fmts)
        {
            return this.PrintIndented(color, indention, text + "\r\n", fmts);
        }

        public CommandBase PrintKeyValue (string key, object value)
        {
            return this.Print(ConsoleColor.Red, (key + ":").PadRight(15))
                .PrintLine(ConsoleColor.Gray, Convert.ToString(value));
        }

        public CommandBase PrintDelimiter (ConsoleColor color)
        {
            return this.Print(color, "".PadRight(Console.WindowWidth, '-'));
        }

        public CommandBase PrintObject(object obj)
        {
            return PrintObject(obj, 0).PrintLine("");
        }

        private CommandBase PrintObject(object obj, int indent)
        {
            if (obj == null)
                return this.Print(ConsoleColor.Magenta, "null");

            Type type = obj.GetType();

            if (type.IsValueType)
                this.Print(ConsoleColor.White, Convert.ToString(obj));
            else if (type == typeof(string))
                this.Print(ConsoleColor.White, "\"{0}\"", Convert.ToString(obj));
            else if (type.ToString().Equals("system.runtimetype", StringComparison.OrdinalIgnoreCase) || type == typeof(Type))
            {
                Console.BackgroundColor = ConsoleColor.DarkGray;
                this.Print(ConsoleColor.Black, ((Type)obj).ToString());
                Console.BackgroundColor = ConsoleColor.Black;
            }
            else if (type.IsArray || typeof(IList).IsAssignableFrom(type))
            {
                this.PrintLine(ConsoleColor.DarkRed, @"[");
                indent += 2;
                bool first = true;
                foreach (object o in (IEnumerable)obj)
                {
                    if (!first)
                        this.PrintLine(ConsoleColor.Gray, ",");
                    this.PrintIndented(indent, "").PrintObject(o, indent);
                    first = false;
                }
                this.PrintLine("");
                indent -= 2;
                this.PrintIndented(ConsoleColor.DarkRed, indent, @"]");
            }
            else if (typeof(IDictionary).IsAssignableFrom(type))
            {
                this.PrintLine(ConsoleColor.Yellow, @"(");
                indent += 2;
                foreach (DictionaryEntry entry in (IDictionary)obj)
                {
                    string key = Convert.ToString(entry.Key);
                    this.PrintIndented(ConsoleColor.Gray, indent, (key.Length > 18) ? key.Substring(0, 18) : key.PadRight(18))
                        .Print(ConsoleColor.Gray, "= ")
                        .PrintObject(((IDictionary)obj)[entry.Key], indent + 20)
                        .PrintLine("");
                }
                indent -= 2;
                this.PrintIndented(ConsoleColor.Yellow, indent, @")");
            }
            else
            {
                this.PrintLine(ConsoleColor.DarkGreen, @"{{ {0}", type.ToString());
                indent += 2;
                foreach (PropertyInfo property in type.GetProperties())
                {
                    this.PrintIndented(ConsoleColor.Gray, indent, (property.Name.Length > 18) ? property.Name.Substring(0, 18) : property.Name.PadRight(18))
                        .Print(ConsoleColor.Gray, "= ")
                        .PrintObject(property.GetValue(obj, null), indent + 20)
                        .PrintLine("");
                }
                indent -= 2;
                this.PrintIndented(ConsoleColor.DarkGreen, indent, @"}}");
            }
            return this;
        }

        public char Read ()
        {
            return Convert.ToChar(Console.Read());
        }

        public string ReadLine ()
        {
            return Console.ReadLine();
        }

        public string ReadLine (string prefix, params object[] fmts)
        {
            this.Print(prefix, fmts);
            return this.ReadLine();
        }

        public string ReadLine (ConsoleColor color, string prefix, params object[] fmts)
        {
            this.Print(color, prefix, fmts);
            return this.ReadLine();
        }

        public void Catch (Action action)
        {
            try
            {
                action();
            }
            catch (Exception exception)
            {
                this.PrintException(exception);
            }
        }

        public CommandBase Run (string cmd)
        {
            this.PrintLine(ConsoleColor.Cyan, "> {0}", cmd);
            Program.Instance.RunCommand(cmd);
            return this;
        }

        public void VerifyStep (string text, Func<bool> func)
        {
            int width = Console.WindowWidth;
            text = text.PadRight(width - 8, '.');
            this.Print(ConsoleColor.Gray, text);
            try
            {
                if (func())
                    this.PrintLine(ConsoleColor.Green, "OK");
                else
                    this.PrintLine(ConsoleColor.Red, "NO");
            }
            catch (Exception exception)
            {
                this.PrintLine(ConsoleColor.DarkRed, "!!");
                this.PrintException(exception);
            }
        }

        public void VerifyStep (string text, Action action)
        {
            this.VerifyStep(text, () =>
            {
                action();
                return true;
            });
        }
    }
}
